function UserInfo(){
    this.users = {};
    this.maxLevel = 120;
}

UserInfo.prototype = {
    init : function(callback) {
        var maxLevel = 13;
        var cursor = gDBUser.find({'status.level':{$gte:13}},{'_id':1, 'info':1, 'status':1, 'role':1, 'team':1});
        cursor.each(function(err, item){
            if( cursor.isClosed() ) {
                this.maxLevel = maxLevel;
                callback && callback(true);
            }

            if( err ) {
                ERROR(util.format('LOAD USERINFO %j', err));
                callback && callback(false);
            }

            if( !item )  return;

            var roles = {};
            for( var pos in item.team ) {
                var rid = item.team[pos];
                var role = item.role[rid];
                if( role ) {
                    roles[rid] = role.fight_force;
                }
            }

            this.users[item._id] = {
                'name' : item.info.un,
                'headpic' : item.info.headpic,
                'blue' : item.info.blue || 0,
                'yellow': item.info.yellow || 0,
                'blue_year': item.info.blue_year || 0,
                'yellow_year': item.info.yellow_year || 0,
                'level' : item.status.level,
                'fight_force' : item.status.fight_force,
                'roles': roles,
            };
            if( item.status.level > maxLevel ) {
                maxLevel = item.status.level;
            }
        }.bind(this));
    },

    getUser : function(uid) {
        var user = this.users[uid];
        if( !user ) {
            user = {
                'name' : '',
                'headpic' : '',
                'level' : 6,
                'blue' : 0,
                'yellow': 0,
                'blue_year': 0,
                'yellow_year': 0,
                'fight_force' : 1000,
                'roles':{
                    48:100
                },
            };
            this.users[uid] = user;
        }

        user.rank = gRank.users[uid] || 0;
        user.league = gLeague.users[uid] || '';

        return user;
    },

    update : function(uid, userinfo) {
        var existUserInfo = this.getUser(uid);
        for( var key in userinfo ) {
            existUserInfo[key] = userinfo[key];
        }

        if( userinfo.invited ) {
            gInvite.update(uid, userinfo.invited);
        }
    },

    updateRole : function(uid, roles, fight_force) {
        var userinfo = this.getUser(uid);
        userinfo.roles = roles;
        userinfo.fight_force = fight_force;
    },

    updatePlayer : function(player){
        var user = player.user;
        var userinfo = this.getUser(player.uid);

        var roles = {};
        var fightForceSum = 0;
        for( var pos in user.team ) {
            var rid = user.team[pos];
            if( !rid ) {
                continue;
            }

            var fightForce = user.role[rid].fight_force;
            roles[rid] = fightForce;
            fightForceSum += fightForce;
        }

        userinfo.name = user.info.un;
        userinfo.headpic = user.info.headpic;
        userinfo.level = user.status.level;
        userinfo.fight_force = fightForceSum;
        userinfo.roles = roles;
        userinfo.blue = user.info.blue;
        userinfo.yellow = user.info.yellow;
        userinfo.blue_year = user.info.blue_year;
        userinfo.yellow_year = user.info.yellow_year;
    },
}

function Invite(){
    this.invites = {        // 邀请情况
        // uid : []
    };

    this.goals =  {         // 目标达成情况
    };

    this.updates = {};
}

Invite.create = function() {
    gDBWorld.insert({_id:'invite', invite:{}}, function(err, result){});
};

Invite.prototype = {
    init : function(callback) {

        // 读取邀请数据
        gDBWorld.findOne({_id : 'invite'}, {}, function(err, doc){
            if( doc ) {
                this.invites = doc.invite;
                callback && callback(true);
            }else{
                callback && callback(false);
            }
        }.bind(this));
    },

    markDirty : function(uid) {
        this.updates['invite.'+uid] = this.invites[uid] || [];
    },

    save : function(force, callback) {
        var updates = this.updates;
        this.updates = {};
        
        gDBWorld.update({_id : 'invite'}, {$set:updates}, function(err, result){
            if( err ) {
                ERROR(util.format('SAVE INVITE: %j %j', updates, err));
                callback && callback(false);
            }else {
                LOG(util.format('SAVE INVITE: %j', updates));
                callback && callback(true);
            }
        });
    },

    update : function(uid, invited) {
        var allInvited = this.invites[invited] || [];
        if( allInvited.indexOf(uid) < 0 ) {
            allInvited.push(uid);
            this.invites[invited] = allInvited;
            this.markDirty(invited);

            this.save();
        }
    },

    getInviteGoal : function(uid, callback) {
        var allInvited = this.invites[uid];
        if( !allInvited || allInvited.length == 0 ) {
            callback && callback({}, {});
            return;
        }
        gDBUser.find({_id:{"$in":allInvited}}, {'status.level':1, 'activity.paid':1, 'mark.login_days':1}).toArray(function(err, results){
            var goal = {};
            var infos = {};
            if( !err ) {
                results.forEach(function(item) {
                    infos[item._id] = {
                       'name': gUserInfo.getUser(item._id).name,
                       'paid': item.activity.paid,
                       'level': item.status.level,
                       'login' : item.mark.login_days,
                    }; 
                });

                for( var id in gConfInviteGoal ) {
                    var conf = gConfInviteGoal[id];
                    if( conf.GoalType == 'Invite' ) {
                        goal[id] = results.length; 
                    }else if( conf.GoalType == 'LevelUp' ) {
                        var count = 0;
                        results.forEach(function(item){
                            if( item.status.level >= conf.GoalTarget ) {
                                count += 1;
                            }

                        });

                        goal[id] = count;
                    }else if( conf.GoalType == 'Paid' ) {
                        var count = 0;
                        results.forEach(function(item){
                            if( item.activity.paid >= conf.GoalTarget ) {
                                count += 1;
                            }
                        });
                        goal[id] = count;
                    }else if( conf.GoalType == 'Login' ) {
                        var count = 0;
                        results.forEach(function(item){
                            if( item.mark.login_days >= conf.GoalTarget ) {
                                count += 1;
                            }
                        });
                        goal[id] = count;
                    }
                }

                this.goals[uid] = goal;
            }

            callback && callback(this.goals[uid] || {}, infos);
        }.bind(this));
    },
};

exports.update = function(req, resp, onHandled) {
    gUserInfo.update(req.uid, req.args);
    
    onHandled();
}

exports.get_invite = function(req, resp, onHandled) {
    gInvite.getInviteGoal(+req.uid, function(goal, infos){
        resp.data.invite = goal;
        resp.data.infos = infos; 
        onHandled();
    });
}

// 好友系统
function Friend() {
    this.users = {
        /*uid : {               // 自己的UID
            applied : { 
                uid : time,     // 等待处理的申请玩家UID : 申请时间
            },
            friend : {        
                uid : 1,        // 好友 
            },
            shield : {        
                uid : 1,        // 屏蔽 
            },
            black : {
                uid : 1,        // 黑名单 
            },
        },*/
    };

    this.updates = {};
}

Friend.create = function() {
    gDBWorld.insert({_id:'friend', user:{}}, function(err, result){});
};

Friend.prototype = {
    init : function(callback) {

        // 读取邀请数据
        gDBWorld.findOne({_id : 'friend'}, {}, function(err, doc){
            if( doc ) {
                this.users = doc.user;
                callback && callback(true);
            }else{
                callback && callback(false);
            }
        }.bind(this));
    },

    markDirty : function(uid, name) {
        this.updates[util.format('user.%d.%s',uid, name)] = this.users[uid][name];
    },

    save : function(force, callback) {
        if( !force && Object.keys(this.updates).length < 10 ) {
            callback && callback(true);
            return; 
        }

        var updates = this.updates;
        this.updates = {};
        
        gDBWorld.update({_id :'friend'}, {$set:updates}, function(err, result){
            if( err ) {
                ERROR(util.format('SAVE INVITE: %j %j', updates, err));
                callback && callback(false);
            }else {
                LOG(util.format('SAVE INVITE: %j', updates));
                callback && callback(true);
            }
        });
    },
    
    getUser : function(uid) {
        if( !(uid in this.users) ) {
            this.users[uid] = {
                'applied' : {},
                'friend' : {},
                'shield' : {},
                'black' : {},
            }    
            this.markDirty(uid, 'applied');
            this.markDirty(uid, 'friend');
            this.markDirty(uid, 'shield');
            this.markDirty(uid, 'black');
        }

        return this.users[uid];  
    },

    applyFriend : function(uid, friendId) {
        this.users[friendId].applied[uid] = common.getTime(); 
        this.markDirty(friendId, 'applied');

        gMessage.setFriendUpdate(friendId);
    },

    deleteApply : function(uid, friendId) {
        var userInfo = this.users[uid];
        if( friendId in userInfo.applied ) {
            delete userInfo.applied[friendId]; 
            this.markDirty(uid, 'applied');
        }
    },

    addFriend : function(uid, friendId) {
        this.users[uid].friend[friendId] = 1
        this.users[friendId].friend[uid] = 1
        this.markDirty(friendId, 'friend');
        this.markDirty(uid, 'friend');
    },

    deleteFriend : function(uid, friendId) {
        var userInfo = this.users[uid];
        if( friendId in userInfo.friend ) {
            delete userInfo.friend[friendId]; 
            this.markDirty(uid, 'friend');
        }
        
        var friendInfo = this.users[friendId];
        if( uid in friendInfo.friend ) {
            delete friendInfo.friend[uid]; 
            this.markDirty(friendId, 'friend');
        }
    },
    
    shieldFriend : function(uid, friendId) {
        this.users[uid].shield[friendId] = 1
        this.markDirty(uid, 'shield');
    },

    deleteShield: function(uid, friendId) {
        var userInfo = this.users[uid];
        if( friendId in userInfo.shield ) {
            delete this.users[uid].shield[friendId];
            this.markDirty(uid, 'shield');
        }
    },

    blackFriend : function(uid, friendId) {
        this.users[uid].black[friendId] = 1
        this.markDirty(uid, 'black');
    },

    deleteBlack : function(uid, friendId) {
        var userInfo = this.users[uid];
        if( friendId in userInfo.black ) {
            delete this.users[uid].black[friendId];
            this.markDirty(uid, 'black');
        }
    },

    checkApplied : function(uid) {
        var applied = this.users[uid].applied; 
        var now = common.getTime();
        var dirty = false;
        for( var id in applied ) {
            if( now - applied[id] < 86400 ) continue; 
            delete applied[id];
            dirty = true;
        }

        if( dirty ) {
            this.markDirty(uid,'applied'); 
        }
    },

    isFriend: function(myUid, fUid) {
        var userInfo = this.users[myUid] || {};
        var friends = userInfo.friend || {};
        return fUid in friends;
    },

    getFriends: function(uid) {
        var userInfo = this.users[uid] || {};
        return userInfo.friend || {};
    }

}

exports.get_friend = function(req, resp, onHandled) {
    var uid = req.uid;
    do {
        var userInfo = gFriend.getUser(uid);
        gFriend.checkApplied(uid);
        resp.data.friend = {
            'friend' : userInfo.friend,
            'shield' : userInfo.shield,
            'black' : userInfo.black,
        };
    } while(false);
    
    gFriend.save(); 
    onHandled();
}

exports.apply_friend = function(req, resp, onHandled) {
    var uid = req.uid;
    do {
        var friendId = Math.floor(req.args.id);
        var userInfo = gFriend.getUser(uid);
        var friendInfo = gFriend.getUser(friendId);

        if( uid in friendInfo.black ) {
            resp.data.blacked = 1; break;
        }

        if( uid in friendInfo.applied ) {
            resp.data.applied = 1; break;
        }

        if( (friendId in userInfo.friend) || (friendId in userInfo.shield) || 
            (friendId in userInfo.black) ) {
            resp.data.update= 1; break;
        }

        if( Object.keys(userInfo.friend).length >= gConfGlobal.FriendCountLimit ) {
            resp.data.fulled = 'user';break;
        }

        if( Object.keys(friendInfo.friend).length >= gConfGlobal.FriendCountLimit ) {
            resp.data.fulled = 'friend';break;
        }
        
        if( friendId in userInfo.applied ) {
            // 如果申请对方为好友时,对方也申请了你,则通过申请
            gFriend.deleteApply(uid, friendId);
            if( uid in friendInfo.shield ) {
                gFriend.deleteShield(friendId, uid);
            }

            gFriend.addFriend(uid, friendId);

            // 给对方的消息
            gMessage.setFriendUpdate(friendId);
            var message = {
                'type' : 'friend',
                'name' : gUserInfo.getUser(uid).name,
                'uid' : uid,
                'time' : common.getTime(),
                'agree' :1, 
                'apply' :0,
            };
            gMessage.addMessage(friendId, message);


            // 给自己的消息
            gMessage.setFriendUpdate(uid);
            var message = {
                'type' : 'friend',
                'name' : gUserInfo.getUser(friendId).name,
                'uid' : friendId,
                'time' : common.getTime(),
                'agree' :1, 
                'apply' :0,
            };
            gMessage.addMessage(uid, message);
        }else {
            // 向对方发出申请
            gFriend.applyFriend(uid, friendId); 
        }

    } while(false);
    
    gFriend.save(); 
    onHandled();
}

exports.handle_apply_friend = function(req, resp, onHandled) {
    var uid = req.uid;
    do {
        var friendId = req.args.id;
        var userInfo = gFriend.getUser(uid);
        var friendInfo = gFriend.getUser(friendId);
        if( (friendId in userInfo.friend) || (uid in friendInfo.friend) ) {
            resp.data.friended = 1; break; 
        }else if( (friendId in userInfo.black) || uid in friendInfo.black ) {
            resp.data.blacked = 1; break; 
        }

        if( !(friendId in userInfo.applied) ) {
            resp.data.update = 1; break;
        }

        var agree = req.args.agree ? true : false;
        gFriend.deleteApply(uid, friendId);

        if( agree ) {
            if( Object.keys(userInfo.friend).length >= gConfGlobal.FriendCountLimit ) {
                resp.data.fulled = 'user';break;
            }

            if( Object.keys(friendInfo.friend).length >= gConfGlobal.FriendCountLimit ) {
                resp.data.fulled = 'friend';break;
            }

            if( friendId in userInfo.shield ) {
                gFriend.deleteShield(uid, friendId);
            }

            if( uid in friendInfo.shield ) {
                gFriend.deleteShield(friendId, uid);
            }

            if( uid in friendInfo.applied ) {
                gFriend.deleteApply(friendId, uid);
            } 

            gFriend.addFriend(uid, friendId);
        }
         
        // 给对方的消息
        gMessage.setFriendUpdate(friendId);
        var message = {
            'type' : 'friend',
            'name' : gUserInfo.getUser(uid).name,
            'uid' : uid,
            'time' : common.getTime(),
            'agree' :agree, 
            'apply' :0,
        };
        gMessage.addMessage(friendId, message);
        
        resp.data.succ = 1;
    } while(false);
    
    gFriend.save(); 
    onHandled();
}

exports.shield_friend = function(req, resp, onHandled) {
    var uid = req.uid;
    do {
        var friendId = req.args.id;
        var userInfo = gFriend.getUser(uid);
        var friendInfo = gFriend.getUser(friendId);

        if( (friendId in userInfo.shield) || (friendId in userInfo.black) ) {
            resp.data.update= 1; break;
        }

        if( Object.keys(userInfo.shield).length >= gConfGlobal.FriendShieldLimit  ) {
            resp.data.fulled = 1; break;
        }

        if( friendId in userInfo.friend ) {
            gFriend.deleteFriend(uid, friendId); 
        }

        gFriend.shieldFriend(uid, friendId);

    } while(false);
    
    gFriend.save(); 
    onHandled();
}

exports.black_friend = function(req, resp, onHandled) {
    var uid = req.uid;
    do {
        var friendId = req.args.id;
        var userInfo = gFriend.getUser(uid);
        var friendInfo = gFriend.getUser(friendId);

        if( (friendId in userInfo.shield) || (friendId in userInfo.black) ) {
            resp.data.update= 1; break;
        }

        if( Object.keys(userInfo.black).length >= gConfGlobal.FriendBlackLimit ) {
            resp.data.fulled = 1; break;
        }

        if( friendId in userInfo.applied ) {
            // 拉黑即拒绝申请
            gFriend.deleteApply(uid, friendId);

            gMessage.setFriendUpdate(friendId);
            var message = {
                'type' : 'friend',
                'name' : gUserInfo.getUser(uid).name,
                'uid' : uid,
                'time' : common.getTime(),
                'agree' :0, 
                'apply' :0,
            };
            gMessage.addMessage(friendId, message);
        }

        if( friendId in userInfo.friend ) {
            gFriend.deleteFriend(uid, friendId); 
        }

        gFriend.blackFriend(uid, friendId);

    } while(false);
    
    gFriend.save(); 
    onHandled();
}

exports.delete_friend = function(req, resp, onHandled) {
    var uid = req.uid;
    do {
        var friendId = Math.floor(req.args.id);
        var deleteType = req.args.type;
        var deleteTypeMap = {
            'friend' : 1,
            'shield' : 1,
            'black' : 1,
        };

        if( !(deleteType in deleteTypeMap) ) {
            resp.code = 1; resp.desc = 'type error'; break;
        }

        var userInfo = gFriend.getUser(uid);
        if( !((friendId in userInfo.friend) || (friendId in userInfo.shield) ||
            (friendId in userInfo.black)) ) {
            resp.data.update = 1;break;
        }
        
        if( 'friend' == deleteType ) {
            gFriend.deleteFriend(uid, friendId); 
        }else if( 'shield' == deleteType ) {
            gFriend.deleteShield(uid, friendId); 
        }else if( 'black' == deleteType ) {
            gFriend.deleteBlack(uid, friendId); 
        }

    } while(false);
    
    gFriend.save(); 
    onHandled();
}

exports.visit_friend = function(req, resp, onHandled) {
    var uid = req.uid;
    do {
        var friendId = Math.floor(req.args.id);
        var userInfo = gFriend.getUser(uid);
        if( !(friendId in userInfo.friend) ) {
            resp.data.update = 1;
        }

        // 平乱数据
        resp.data.insurgency = (gInsurgency.getFriendSignByUid(uid, friendId) == 1);

    } while(false);
    
    gFriend.save(); 
    onHandled();
}

exports.Invite = Invite;
exports.UserInfo = UserInfo;
exports.Friend = Friend;
